package com.naiterui.ehp.bs.file.api.advice;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.stream.Collectors;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import com.naiterui.ehp.bp.support.advice.IgnoreResponseEntityWrap;
import com.naiterui.ehp.bp.utils.context.SpringContextHolder;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bs.file.api.annotation.SafeUrlTransfer;
import com.naiterui.ehp.bs.file.api.bo.FileBO;
import com.naiterui.ehp.bs.file.api.bo.FileParamBO;
import com.naiterui.ehp.bs.file.api.exception.FileException;
import com.naiterui.ehp.bs.file.api.feign.FileFeignClient;

import cn.hutool.core.collection.CollectionUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * 全局安全链接转换处理
 *
 * @author luao
 * @since 2021/9/15
 */
@Slf4j
@Component
@ControllerAdvice("com.naiterui")
@RequiredArgsConstructor
public class SafeUrlResponseAdvice implements ResponseBodyAdvice<Object> {

    private final static String imgHost = ConfigUtil.getString("ehp.domain.img");

    @Override
    public boolean supports(MethodParameter returnType,
        @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
        Method method = returnType.getMethod();
        if (method == null || method.isAnnotationPresent(IgnoreResponseEntityWrap.class)) {
            return false;
        }
        return !ResponseEntity.class.equals(method.getReturnType());
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, @NonNull MethodParameter returnType,
        @NonNull MediaType selectedContentType, @NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
        @NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response) {

        // if (request.getHeaders().containsKey(SettingConfig.HEADER_INNER)) {
        // return body;
        // }
        //
        // if (body instanceof String) {
        // return body;
        // }
        //
        // if (body instanceof ResponseVO) {
        // ResponseVO<Object> responseVO = (ResponseVO)body;
        // Object data = responseVO.getData();
        // safeUrl(data);
        // return responseVO;
        // }
        // safeUrl(body);
        return body;
    }

    /**
     * 转换安全链接
     * 
     * @param data
     *            response data对象
     */
    public void safeUrl(Object data) throws FileException {
        if (data == null) {
            return;
        }
        List<Object> dataList = new ArrayList<>();
        // 判断responseData类型
        if (data instanceof Collection) {
            dataList.addAll((Collection<?>)data);
        } else {
            dataList.add(data);
        }
        List<String> filePaths = new ArrayList<>();
        List<Field> fields = new ArrayList<>();
        List<Object> objects = new ArrayList<>();
        SafeUrlTransfer safeUrlTransfer = null;
        for (Object dataItem : dataList) {
            Class<?> clazz = dataItem.getClass();
            // 如果类中无@SafeUrlTransfer注解则退出
            if (!clazz.isAnnotationPresent(SafeUrlTransfer.class)) {
                return;
            }
            // 获取需要转换安全链接的Field
            safeUrlField(filePaths, fields, objects, dataItem);
            safeUrlTransfer = clazz.getAnnotation(SafeUrlTransfer.class);
        }
        if (CollectionUtil.isEmpty(filePaths)) {
            return;
        }
        // 处理安全链接
        handleFieldValue(filePaths, fields, objects, safeUrlTransfer);
    }

    /**
     * 获取需要转换安全链接的Filed
     * 
     * @param filePaths
     *            文件path
     * @param fields
     *            fields集
     * @param objects
     *            field对应的对象集
     * @param data
     *            对象
     */
    public static void safeUrlField(List<String> filePaths, List<Field> fields, List<Object> objects, Object data) {
        Class<?> clazz = data.getClass();
        // 获取类所有字段
        List<Field> allFields = new ArrayList<>();
        while (clazz != null) {
            allFields.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        for (Field field : allFields) {
            // 判断Field是否有@SafeUrlTransfer注解
            if (!field.isAnnotationPresent(SafeUrlTransfer.class)) {
                continue;
            }
            ReflectionUtils.makeAccessible(field);
            Object o = null;
            try {
                o = field.get(data);
            } catch (IllegalAccessException e) {
                log.warn("safe url get field value fail", e);
            }
            if (o == null) {
                continue;
            }
            Class<?> fieldType = field.getType();
            // 判断当前Field是否为参数化类型
            if (field.getGenericType() instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType)field.getGenericType();
                // 是否为集合类型
                if (pt.getRawType().equals(List.class) || pt.getRawType().equals(Set.class)) {
                    // 如果集合泛型为String则添加，如果为其他类型则递归往下找
                    if (pt.getActualTypeArguments()[0].equals(String.class)) {
                        Collection<String> collection = (Collection)o;
                        filePaths
                            .addAll(collection.stream().map(s -> s.replace(imgHost, "")).collect(Collectors.toList()));
                        fields.add(field);
                        objects.add(data);
                    } else {
                        List list = (List)o;
                        for (Object o1 : list) {
                            safeUrlField(filePaths, fields, objects, o1);
                        }
                    }
                }
            } else {
                if (fieldType == String.class) {
                    String value = (String)o;
                    filePaths.add(value.replace(imgHost, ""));
                    fields.add(field);
                    objects.add(data);
                } else {
                    safeUrlField(filePaths, fields, objects, o);
                }
            }
        }
    }

    /**
     * 批量处理安全url
     *
     * @param filePaths
     *            文件路径集合
     * @param fields
     *            field集合
     * @param objects
     *            对象集合
     * @throws FileException
     */
    public void handleFieldValue(List<String> filePaths, List<Field> fields, List<Object> objects,
        SafeUrlTransfer safeUrlTransfer) throws FileException {
        // 批量生成安全链接
        Map<String,
            FileBO> fileMap = SpringContextHolder.getBean(FileFeignClient.class)
                .batchGeneratePreview(FileParamBO.builder().filePaths(filePaths).timeCheck(safeUrlTransfer.timeCheck())
                    .ipCheck(safeUrlTransfer.ipCheck()).uaCheck(safeUrlTransfer.uaCheck())
                    .refererCheck(safeUrlTransfer.refererCheck()).build());

        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            Object obj = objects.get(i);
            Object value = null;
            try {
                value = field.get(obj);
            } catch (IllegalAccessException e) {
                log.warn("safe url get field value fail", e);
            }
            if (value == null) {
                continue;
            }
            try {
                if (field.getType().equals(List.class)) {
                    List<String> paths = (List<String>)value;
                    List<String> collect = paths.stream()
                        .map(path -> Optional.ofNullable(fileMap.get(path)).map(FileBO::getUrl).orElse(path))
                        .collect(Collectors.toList());
                    field.set(obj, collect);
                } else if (field.getType().equals(Set.class)) {
                    Set<String> paths = (Set<String>)value;
                    Set<String> collect = paths.stream()
                        .map(path -> Optional.ofNullable(fileMap.get(path)).map(FileBO::getUrl).orElse(path))
                        .collect(Collectors.toSet());
                    field.set(obj, collect);
                } else if (field.getType().equals(String.class)) {
                    String path = (String)value;
                    field.set(obj, Optional.ofNullable(fileMap.get(path)).map(FileBO::getUrl).orElse(path));
                }
            } catch (IllegalAccessException e) {
                log.warn("safe url set field value fail", e);
            }
        }
    }
}
